package com.imyuanma.qingyun.lowcode.util;

import com.imyuanma.qingyun.common.exception.Exceptions;
import com.imyuanma.qingyun.common.util.CollectionUtil;
import com.imyuanma.qingyun.common.util.StringUtil;
import com.imyuanma.qingyun.lowcode.model.bo.GenerateFieldBO;
import com.imyuanma.qingyun.lowcode.model.bo.GenerateMainBO;
import com.imyuanma.qingyun.lowcode.model.data.CodeOnlineSqlDO;
import com.imyuanma.qingyun.lowcode.model.data.FieldAndValueDO;
import com.imyuanma.qingyun.lowcode.model.enums.ELcpColumnTypeEnum;
import com.imyuanma.qingyun.lowcode.model.enums.ELcpQueryConditionTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 代码在线服务工具类
 *
 * @author wangjy
 * @date 2022/07/01 22:33:48
 */
public class CodeOnlineUtil {
    private static final Logger logger = LoggerFactory.getLogger(CodeOnlineUtil.class);
    /**
     * 构造列表查询对象
     *
     * @param generateMainBO 代码生成配置
     * @param condition      查询条件参数
     * @return
     */
    public static CodeOnlineSqlDO buildListQuerySql(GenerateMainBO generateMainBO, Map<String, Object> condition) {
        CodeOnlineSqlDO codeOnlineSqlDO = new CodeOnlineSqlDO();
        // 表名
        codeOnlineSqlDO.setTableName(generateMainBO.getTableName());
        // 查询字段
        codeOnlineSqlDO.setFieldAndValueList(generateMainBO.getFieldList().stream().map(FieldAndValueDO::of).collect(Collectors.toList()));
        // 查询条件
        List<FieldAndValueDO> conditionList = generateMainBO.getFieldList().stream()
                .filter(item -> item.getListQueryCondition() != null)
                // 按照查询条件类型过滤
                .filter(item -> !ELcpQueryConditionTypeEnum.NO.getType().equals(item.getListQueryCondition()))
                // 转条件查询对象
                .map(item -> FieldAndValueDO.of(item, condition))
                // 查询条件存在, 普通条件值不为空, 或者between and条件的end值不为空
                .filter(item -> valueCanAsCondition(item.getValue()) || (ELcpQueryConditionTypeEnum.BETWEEN_AND.getType().equals(item.getQueryConditionType()) && valueCanAsCondition(item.getValue2())))
                .collect(Collectors.toList());
        codeOnlineSqlDO.setConditionList(conditionList);
        // 排序字段
        String sortBy = StringUtil.valueOf(condition.get("dbSortBy"));
        if (StringUtil.isNotBlank(sortBy)) {
            // 前端给到的是java属性, 需要映射为数据库字段名
            GenerateFieldBO generateFieldBO = generateMainBO.getFieldList().stream().filter(item->sortBy.equals(item.getFieldJavaName())).findFirst().orElse(null);
            if (generateFieldBO != null) {
                codeOnlineSqlDO.setDbSortBy(generateFieldBO.getFieldDbName());
                // 排序类型, 转换一下, 防止sql注入
                codeOnlineSqlDO.setDbSortType("desc".equalsIgnoreCase(StringUtil.valueOf(condition.get("dbSortType"))) ? "desc" : "asc");
            } else {
                logger.warn("[构造列表查询对象] 匹配排序字段失败,入参排序字段={}", sortBy);
            }
        }
        return codeOnlineSqlDO;
    }

    /**
     * 构造单查询sql对象
     *
     * @param generateMainBO 代码生成配置
     * @param primaryId      主键值
     * @return
     */
    public static CodeOnlineSqlDO buildGetOneSql(GenerateMainBO generateMainBO, Object primaryId) {
        CodeOnlineSqlDO codeOnlineSqlDO = new CodeOnlineSqlDO();
        // 表名
        codeOnlineSqlDO.setTableName(generateMainBO.getTableName());
        // 查询字段
        codeOnlineSqlDO.setFieldAndValueList(generateMainBO.getFieldList().stream().map(FieldAndValueDO::of).collect(Collectors.toList()));
        // 主键字段
        GenerateFieldBO primaryField = generateMainBO.findPrimaryKeyField();
        codeOnlineSqlDO.setPrimaryKeyName(primaryField.getFieldDbName());
        codeOnlineSqlDO.setPrimaryKeyValue(primaryId);
        if (!valueCanAsCondition(codeOnlineSqlDO.getPrimaryKeyValue())) {
            throw Exceptions.paramException("入参主键值无效[" + primaryField.getFieldJavaName() + "]");
        }
        return codeOnlineSqlDO;
    }

    /**
     * 构造插入sql对象
     *
     * @param generateMainBO 代码生成配置
     * @param condition      查询条件参数
     * @return
     */
    public static CodeOnlineSqlDO buildInsertSelectiveSql(GenerateMainBO generateMainBO, Map<String, Object> condition) {
        CodeOnlineSqlDO codeOnlineSqlDO = new CodeOnlineSqlDO();
        // 表名
        codeOnlineSqlDO.setTableName(generateMainBO.getTableName());
        // 插入字段
        List<FieldAndValueDO> insertFieldList = generateMainBO.getFieldList().stream()
                .map(item -> FieldAndValueDO.of(item, condition))
                // 过滤掉值为null的
                .filter(item -> item.getValue() != null)
                .collect(Collectors.toList());
        codeOnlineSqlDO.setFieldAndValueList(insertFieldList);
        return codeOnlineSqlDO;
    }

    /**
     * 构造更新sql对象
     *
     * @param generateMainBO 代码生成配置
     * @param condition      查询条件参数
     * @return
     */
    public static CodeOnlineSqlDO buildUpdateSelectiveSql(GenerateMainBO generateMainBO, Map<String, Object> condition) {
        CodeOnlineSqlDO codeOnlineSqlDO = new CodeOnlineSqlDO();
        // 表名
        codeOnlineSqlDO.setTableName(generateMainBO.getTableName());
        // 主键字段
        GenerateFieldBO primaryField = generateMainBO.findPrimaryKeyField();
        codeOnlineSqlDO.setPrimaryKeyName(primaryField.getFieldDbName());
        codeOnlineSqlDO.setPrimaryKeyValue(condition.get(primaryField.getFieldJavaName()));
        if (!valueCanAsCondition(codeOnlineSqlDO.getPrimaryKeyValue())) {
            throw Exceptions.paramException("入参主键值无效[" + primaryField.getFieldJavaName() + "]");
        }
        // 更新字段
        List<FieldAndValueDO> insertFieldList = generateMainBO.getFieldList().stream()
                .map(item -> FieldAndValueDO.of(item, condition))
                // 过滤掉值为null的
                .filter(item -> item.getValue() != null)
                // 过滤掉主键
                .filter(item -> !item.getFieldDbName().equals(primaryField.getFieldDbName()))
                .collect(Collectors.toList());
        codeOnlineSqlDO.setFieldAndValueList(insertFieldList);
        return codeOnlineSqlDO;
    }

    /**
     * 构造删除sql对象
     *
     * @param generateMainBO 代码生成配置
     * @param primaryId      主键值
     * @return
     */
    public static CodeOnlineSqlDO buildDeleteSql(GenerateMainBO generateMainBO, String primaryId) {
        CodeOnlineSqlDO codeOnlineSqlDO = new CodeOnlineSqlDO();
        // 表名
        codeOnlineSqlDO.setTableName(generateMainBO.getTableName());
        // 主键字段
        GenerateFieldBO primaryField = generateMainBO.findPrimaryKeyField();
        codeOnlineSqlDO.setPrimaryKeyName(primaryField.getFieldDbName());
        if (ELcpColumnTypeEnum.BIGINT.getJavaType().equals(primaryField.getFieldJavaType())) {
            //long类型
            codeOnlineSqlDO.setPrimaryKeyValue(Long.valueOf(primaryId));
        } else if (ELcpColumnTypeEnum.INT.getJavaType().equals(primaryField.getFieldJavaType())) {
            //int类型
            codeOnlineSqlDO.setPrimaryKeyValue(Integer.valueOf(primaryId));
        } else {
            //字符串类型
            codeOnlineSqlDO.setPrimaryKeyValue(primaryId);
        }
        if (!valueCanAsCondition(codeOnlineSqlDO.getPrimaryKeyValue())) {
            throw Exceptions.paramException("入参主键值无效[" + primaryField.getFieldJavaName() + "]");
        }
        return codeOnlineSqlDO;
    }

    /**
     * 构造删除sql对象
     *
     * @param generateMainBO 代码生成配置
     * @param primaryIdList  主键值集合
     * @return
     */
    public static CodeOnlineSqlDO buildBatchDeleteSql(GenerateMainBO generateMainBO, List<String> primaryIdList) {
        CodeOnlineSqlDO codeOnlineSqlDO = new CodeOnlineSqlDO();
        // 表名
        codeOnlineSqlDO.setTableName(generateMainBO.getTableName());
        // 主键字段
        GenerateFieldBO primaryField = generateMainBO.findPrimaryKeyField();
        codeOnlineSqlDO.setPrimaryKeyName(primaryField.getFieldDbName());
        if (ELcpColumnTypeEnum.BIGINT.getJavaType().equals(primaryField.getFieldJavaType())) {
            //long类型
            codeOnlineSqlDO.setPrimaryKeyValueList(primaryIdList.stream().filter(StringUtil::isNotBlank).map(Long::valueOf).distinct().collect(Collectors.toList()));
        } else if (ELcpColumnTypeEnum.INT.getJavaType().equals(primaryField.getFieldJavaType())) {
            //int类型
            codeOnlineSqlDO.setPrimaryKeyValueList(primaryIdList.stream().filter(StringUtil::isNotBlank).map(Integer::valueOf).distinct().collect(Collectors.toList()));
        } else {
            //字符串类型
            codeOnlineSqlDO.setPrimaryKeyValueList(primaryIdList.stream().filter(StringUtil::isNotBlank).map(String::valueOf).collect(Collectors.toList()));
        }
        if (!valueCanAsCondition(codeOnlineSqlDO.getPrimaryKeyValueList())) {
            throw Exceptions.paramException("入参主键值无效[" + primaryField.getFieldJavaName() + "]");
        }
        return codeOnlineSqlDO;
    }

    /**
     * 判断值是否可以作为查询条件
     *
     * @param value
     * @return
     */
    private static boolean valueCanAsCondition(Object value) {
        if (value instanceof String) {
            return StringUtil.isNotBlank((String) value);
        } else if (value instanceof Collection) {
            return CollectionUtil.isNotEmpty((Collection) value);
        } else if (value instanceof Object[]) {
            return CollectionUtil.isNotEmpty((Object[]) value);
        } else {
            return value != null;
        }
    }

}
